/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::{error::Error, sync::{OnceLock}};
use libloading::{Library, Symbol};

pub const EVIDENCE_TYPE_EVIDENCE_TYPE_NONE: u64 = 0;
pub const EVIDENCE_TYPE_EVIDENCE_TYPE_QUOTE: u64 = 1;
pub const EVIDENCE_TYPE_EVIDENCE_TYPE_BOOT_MEASUREMENTS: u64 = 2;
pub const EVIDENCE_TYPE_EVIDENCE_TYPE_RUNTIME_MEASUREMENTS: u64 = 4;

/// # AttestCtx
/// 
/// Context structure for DCMI attestation operations.
/// 
/// # Fields
/// - `device_id`: Device ID for attestation.
/// - `card_id`: Card ID for attestation.
/// - `logic_device_id`: Logic device ID for attestation.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AttestCtx {
    pub device_id: ::std::os::raw::c_uint,
    pub card_id: ::std::os::raw::c_uint,
    pub logic_device_id: ::std::os::raw::c_uint,
}

/// # AscendNpuApi
/// 
/// Structure to hold function pointers for DCMI attestation API.
/// 
/// # Fields
/// - `dcmi_init`: Function pointer for `dcmi_init`.
/// - `dcmi_get_card_list`: Function pointer for `dcmi_get_card_list`.
/// - `dcmi_attest_get_evidence`: Function pointer for `dcmi_attest_get_evidence`.
/// - `dcmi_attest_get_akcert`: Function pointer for `dcmi_attest_get_akcert`.
#[derive(Debug)]
pub struct AscendNpuApi {
    pub dcmi_init: Symbol<'static, unsafe extern "C" fn() -> ::std::os::raw::c_int>,
    pub dcmi_get_card_list: Symbol<'static, unsafe extern "C" fn(card_num: *mut ::std::os::raw::c_int, card_list: *mut ::std::os::raw::c_int, list_len: ::std::os::raw::c_int) -> ::std::os::raw::c_int>,
    pub dcmi_attest_get_evidence: Symbol<'static, unsafe extern "C" fn(
        ctx: *mut AttestCtx,
        challenge: *mut ::std::os::raw::c_uchar,
        challenge_len: u32,
        evidence: *mut ::std::os::raw::c_uchar,
        evidence_len: *mut u32,
        collection_mask: u64,
    ) -> ::std::os::raw::c_int>,
    pub dcmi_attest_get_akcert: Symbol<'static, unsafe extern "C" fn(
        ctx: *mut AttestCtx,
        ak_cert: *mut ::std::os::raw::c_uchar,
        ak_cert_len: *mut u32,
    ) -> ::std::os::raw::c_int>,
}

/// # ASCEND_NPU_API_CONFIG
/// 
/// Global static variable to hold the loaded DCMI attestation API functions.
/// 
/// # Safety
/// 
/// This variable is unsafe to access directly. Use the `ASCEND_NPU_API_CONFIG` function to access the API.
pub static ASCEND_NPU_API_CONFIG: OnceLock<AscendNpuApi> = OnceLock::new();

impl AscendNpuApi {
    /// # new
    /// 
    /// Creates a new `AscendNpuApi` instance by loading symbols from a DCMI library.
    /// 
    /// # Parameters
    /// - `lib`: A reference to the loaded DCMI library.
    /// 
    /// # Errors
    /// 
    /// This function will return an error if:
    /// - Any of the required DCMI symbols cannot be loaded from the library.
    /// 
    /// # Returns
    /// A `Result` containing the `AscendNpuApi` instance or a `Box<dyn Error>`.
    pub fn new(lib: &'static Library) -> Result<Self, Box<dyn Error>> {
        Ok(Self {
            dcmi_init: unsafe { lib.get(b"dcmi_init") }?,
            dcmi_get_card_list: unsafe { lib.get(b"dcmi_get_card_list") }?,
            dcmi_attest_get_evidence: unsafe { lib.get(b"dcmi_attest_get_evidence") }?,
            dcmi_attest_get_akcert: unsafe { lib.get(b"dcmi_attest_get_akcert") }?,
        })
    }
}

/// # init_ascend_npu_api
/// 
/// Initializes the DCMI attestation API by loading symbols from a specified library path.
/// 
/// # Parameters
/// - `path`: The path to the DCMI library.
/// 
/// # Errors
/// 
/// This function will return an error if:
/// - The library cannot be loaded.
/// - Any of the required DCMI symbols cannot be loaded from the library.
/// 
/// # Returns
/// A `Result` indicating success or an error message.
pub unsafe fn init_ascend_npu_api(path: &str) -> Result<(), Box<dyn Error>> {
    let lib = Box::new(Library::new(path.to_string())?);
    let lib: &'static Library = Box::leak(lib);
    let api = AscendNpuApi::new(lib)?;
    match ASCEND_NPU_API_CONFIG.set(api) {
        Ok(_) => Ok(()),
        Err(_e) => Err("Failed to set ASCEND_NPU_API_CONFIG".to_string().into()),
    }
}